package org.feifei.user.service.impl;


import com.alibaba.fastjson.JSONObject;
import org.feifei.basic.constant.VerifyConstant;
import org.feifei.basic.exception.BusinessException;
import org.feifei.basic.service.impl.BaseServiceImpl;
import org.feifei.basic.util.MD5Utils;
import org.feifei.basic.util.StrUtils;
import org.feifei.user.domain.LoginInfo;
import org.feifei.user.domain.User;
import org.feifei.user.dto.UserDTO;
import org.feifei.user.mapper.LoginInfoMapper;
import org.feifei.user.mapper.UserMapper;
import org.feifei.user.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * UserService 的实现类
 */
@Transactional(readOnly = false, propagation = Propagation.SUPPORTS)
@Service
public class UserServiceImpl extends BaseServiceImpl<User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private LoginInfoMapper loginInfoMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    RedisTemplate redisTemplate;

    @Override
    public void verifyCode(String phone) {
        // 判断该验证码是否为null 或者 满不满足格式
        if (!StringUtils.hasLength(phone) || !phone.matches("^1[3-9]\\d{9}$")) {
            throw new BusinessException("该手机号不符合规范");
        }
        // 从redis中获取验证码
        String verifyCode = stringRedisTemplate.opsForValue().get(VerifyConstant.REGISTER_CODE_PREFIX + phone);
        String code = null;
        if (verifyCode != null) {
            // 分割验证码获取 随机码和 时间戳
            String[] codes = verifyCode.split(VerifyConstant.VERIFY_SEPARATOR);
            // 判断该次请求是否违约，即没有超过60s
            if (System.currentTimeMillis() - Long.parseLong(codes[1]) < VerifyConstant.CODE_REQUESET_TIMEOUT) {
                throw new BusinessException("验证码请求错误，请稍后再试");
            }
            code = codes[0];
        } else {
            // 如果redis中没有查到，即可以认为是第一次发送验证码，先查询数据库
            User user = userMapper.findUserByPhone(phone);
            if (user != null) {
                throw new BusinessException("该手机号已经被注册,请不要重复注册");
            } else {
                code = StrUtils.getRandomString(4);
            }
        }
        // 将验证码存储到redis
        stringRedisTemplate.opsForValue().set(VerifyConstant.REGISTER_CODE_PREFIX + phone, code + VerifyConstant
                .VERIFY_SEPARATOR + System.currentTimeMillis(), 3, TimeUnit.MINUTES);
        System.out.println("当前验证码是：" + code);
        // TODO 调用发送短信的方法
    }

    @Override
    @Transactional
    public void registerByPhone(UserDTO userDto) {
        String code = userDto.getCode();
        String phone = userDto.getPhone();
        String password = userDto.getPassword();
        String rePassword = userDto.getRePassword();

        // 非空判断
        if (!StringUtils.hasText(phone)
                || !StringUtils.hasText(code)
                || !StringUtils.hasText(password)
                || !StringUtils.hasText(rePassword)
                || !phone.matches("^1[3-9]\\d{9}$")) {
            throw new BusinessException("数据输入正确,请重新输入");
        }
        // 比较两次密码输入是否一致
        if (!password.equals(rePassword)) {
            throw new BusinessException("两次密码输入不一致");
        }
        String verifyCode = stringRedisTemplate.opsForValue().get(VerifyConstant.REGISTER_CODE_PREFIX + phone);
        // 判断当前手机号的验证码是否还在有效期
        if (verifyCode != null) {
            String[] codes = verifyCode.split(VerifyConstant.VERIFY_SEPARATOR);
            // 校验验证码是否一致
            if (!codes[0].equals(code)) {
                throw new BusinessException("验证码输入不正确");
                // 存库之前判断该手机号是否被注册
            } else if (userMapper.findUserByPhone(phone) != null) {
                throw new BusinessException("该手机号已经被注册");
            }
        } else {
            throw new BusinessException("验证码已经过期，请重新获取");
        }
        // 将用户信息注册复制给 登录列表
        LoginInfo loginInfo = userDto2LoginInfo(userDto);
        loginInfoMapper.save(loginInfo);
        // 将登录列表的信息复制给用户表
        User user = loginInto2User(loginInfo);
        userMapper.save(user);

    }

    @Override
    public void bindUserByPhone(String phone ) {
        // 判断该验证码是否为null 或者 满不满足格式
        if (!StringUtils.hasLength(phone) || !phone.matches("^1[3-9]\\d{9}$")) {
            throw new BusinessException("该手机号不符合规范");
        }
        // 从redis中获取验证码
        String verifyCode = (String) redisTemplate.opsForValue().get(VerifyConstant.Binder_CODE_PREFIX + phone);
        String code = null;
        if (verifyCode != null) {
            // 分割验证码获取 随机码和 时间戳
            String[] codes = verifyCode.split(VerifyConstant.VERIFY_SEPARATOR);
            // 判断该次请求是否违约，即没有超过60s
            if (System.currentTimeMillis() - Long.parseLong(codes[1]) < VerifyConstant.CODE_REQUESET_TIMEOUT) {
                throw new BusinessException("验证码请求错误，请稍后再试");
            }
            code = codes[0];
        } else {
            // 如果redis中没有查到，即可以认为是第一次发送验证码，先查询数据库
            User user = userMapper.findUserByPhone(phone);
            if (user != null) {
                throw new BusinessException("该手机号已经被注册,请不要重复注册");
            } else {
                code = StrUtils.getRandomString(4);
            }
        }
        // 将验证码存储到redis
        redisTemplate.opsForValue().set(VerifyConstant.Binder_CODE_PREFIX + phone, code + VerifyConstant
                .VERIFY_SEPARATOR + System.currentTimeMillis(), 3, TimeUnit.MINUTES);
        System.out.println("当前验证码是：" + code);
        // TODO 调用发送短信的方法
    }

    @Override
    public void updateState(Map<String, Object> map) {
        Integer id = (Integer) map.get("id");
        Integer state = (Integer) map.get("state");
        userMapper.updateState(id, state);
    }

    @Override
    public void verifyCodeLoginByPhone(String phone) {
        // 判断该验证码是否为null 或者 满不满足格式
        if (!StringUtils.hasLength(phone) || !phone.matches("^1[3-9]\\d{9}$")) {
            throw new BusinessException("该手机号不符合规范");
        }
        // 从redis中获取验证码
        String verifyCode = stringRedisTemplate.opsForValue().get(VerifyConstant.REGISTER_CODE_PREFIX + phone);
        String code = null;
        if (verifyCode != null) {
            // 分割验证码获取 随机码和 时间戳
            String[] codes = verifyCode.split(VerifyConstant.VERIFY_SEPARATOR);
            // 判断该次请求是否违约，即没有超过60s
            if (System.currentTimeMillis() - Long.parseLong(codes[1]) < VerifyConstant.CODE_REQUESET_TIMEOUT) {
                throw new BusinessException("验证码请求错误，请稍后再试");
            }
            code = codes[0];
        } else {
            // 如果redis中没有查到，即可以认为是第一次发送验证码，先查询数据库
            User user = userMapper.findUserByPhone(phone);
            if (user == null) {
                throw new BusinessException("该手机号还没注册，请前往注册");
            } else {
                code = StrUtils.getRandomString(4);
            }
        }
        // 将验证码存储到redis
        stringRedisTemplate.opsForValue().set(VerifyConstant.REGISTER_CODE_PREFIX + phone, code + VerifyConstant
                .VERIFY_SEPARATOR + System.currentTimeMillis(), 3, TimeUnit.MINUTES);
        System.out.println("当前验证码是：" + code);
        // TODO 调用发送短信的方法
    }


    /**
     * 根据id移除该对象，同时移除该对象的关联对象
     * @param id 用户id
     */
    @Override
    @Transactional
    public void remove(Long id) {
        User user = super.get(id);
        loginInfoMapper.remove(user.getLogininfo_id());
        super.remove(id);
    }

    /**
     * 修改该对象，同时修改该对象关联对象的相关属性
     * @param user
     */
    @Transactional
    @Override
    public void update(User user) {
        // 重新加密密码
        String md5 = StrUtils.getComplexRandomString(32);
        String encrypPassword = MD5Utils.encrypByMd5(md5 + user.getPassword());
        user.setPassword(encrypPassword);
        // 将user的信息复制给logininfo
        LoginInfo loginInfo = user2LoginInfo(user);
        // 重新查询数据库，获取logininfo的id
        loginInfo.setId(super.get(user.getId()).getLogininfo_id());
        loginInfoMapper.update(loginInfo);
        super.update(user);
    }

    /**
     * 将登录信息表的数据复制给用户user
     * @param loginInfo
     * @return
     */
    private User loginInto2User(LoginInfo loginInfo) {
        User user = new User();
        // 复制
        BeanUtils.copyProperties(loginInfo, user);
        user.setLogininfo_id(loginInfo.getId());
        return user;
    }

    /**
     * 将userDTO的数据复制给loginInfo表
     * @param userDto
     * @return
     */
    private LoginInfo userDto2LoginInfo(UserDTO userDto) {
        LoginInfo loginInfo = new LoginInfo();
        // 对密码进行加密
        String md5 = StrUtils.getComplexRandomString(32);
        String encrypPassword = MD5Utils.encrypByMd5(md5 + userDto.getPassword());
        // 更新密码
        loginInfo.setSalt(md5).setPassword(encrypPassword).setPhone(userDto.getPhone()).setUsername(userDto.getPhone());
        return loginInfo;
    }

    /**
     * 复制 员工user的信息 到 logininfo登录信息表
     * @param user
     * @return
     */
        private LoginInfo user2LoginInfo(User user) {
        LoginInfo loginInfo = new LoginInfo();
        BeanUtils.copyProperties(user, loginInfo);
        return loginInfo;
    }
}

